import os
import shutil

from django.contrib.auth.hashers import BasePasswordHasher, mask_hash
from django.utils.crypto import constant_time_compare
from django.utils.translation import gettext_noop as _
from collections import OrderedDict

import hashlib
import base64
import re


class LDAPMD5PasswordHasher(BasePasswordHasher):
    """加密方式 {MD5}+密码的md5散列的二进制形式的Base64值"""
    algorithm = 'ldap_md5'

    def salt(self):
        return ''

    def encode(self, password, salt):
        assert salt == ''
        return '{MD5}%s' % base64.b64encode(hashlib.md5(password.encode()).digest()).decode('ascii')

    def verify(self, password, encoded):
        if not encoded.startswith('{MD5}'):
            return False
        # encoded_2 = self.encode(password, '')
        encoded_2 = '{MD5}' + password
        return constant_time_compare(encoded, encoded_2)

    def safe_summary(self, encoded):
        return OrderedDict([
            (_('algorithm'), self.algorithm),
            (_('hash'), mask_hash(encoded, show=3)),
        ])

    def harden_runtime(self, password, encoded):
        pass


class LDAPSHA1PasswordHasher(BasePasswordHasher):
    """加密方式 {SHA}+密码的sha1散列的二进制形式的Base64值"""
    algorithm = 'ldap_sha1'

    def salt(self):
        return ''

    def encode(self, password, salt):
        assert salt == ''
        return '{SHA}%s' % base64.b64encode(hashlib.sha1(password.encode()).digest()).decode('ascii')

    def verify(self, password, encoded):
        if not encoded.startswith('{SHA}'):
            return False
        encoded_2 = self.encode(password, '')
        return constant_time_compare(encoded, encoded_2)

    def safe_summary(self, encoded):
        return OrderedDict([
            (_('algorithm'), self.algorithm),
            (_('hash'), mask_hash(encoded, show=3)),
        ])

    def harden_runtime(self, password, encoded):
        pass


def strip_algorithm(password_with_algorithm):
    """从加密后的密码字符串中剥离掉算法标识，返回密码部分"""
    match = re.match(r'^\{.+\}(.+)$', password_with_algorithm)
    if match:
        # {algorithm}hash形式的密码
        return match.group(1)
    else:
        # algorithm$salt$hash形式或原始hash形式的密码
        return password_with_algorithm.split('$')[-1]
